﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Design;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Reflection.Emit;

namespace MethodPicker
{
    public partial class MethodPickerEditorUI : Form
    {
        #region *** Properties ***
        public const string COLUMN_DESCRIPTION = "MethodDescription";
        public const string COLUMN_VALUE = "MethodHandle";
        public BindingSource bs = new BindingSource();
        public Type[] assemblyTypes;
        public Assembly assembly;
        private string _returnValue;
        private BindingFlags _bindingFlags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;

        public string ReturnValue
        {
            get
            {
                return _returnValue;
            }
            set
            {
                _returnValue = value;
            }

        }
        #endregion


        public void Start(IWindowsFormsEditorService editorService, object value)
        {
            string pathAndMethodHandles = value as string;
            ReturnValue = pathAndMethodHandles;
            if (!string.IsNullOrEmpty(pathAndMethodHandles))
            {
                ClearLists();
                string[] arrValues = pathAndMethodHandles.Split(new char[] { '#' });
                if (arrValues.Length > 0)
                {
                    tbAssemblyName.Text = arrValues[0];
                    if (LoadAssembly())
                    {
                        DataTable dt = new DataTable();
                        dt.Columns.Add(COLUMN_VALUE);
                        dt.Columns.Add(COLUMN_DESCRIPTION);
                        for (int i = 1; i < arrValues.Length; i++)
                        {
                            MethodInfo mi = GetMethodInfoBySignature(arrValues[i]);
                            if (mi != null)
                            {
                                dt.Rows.Add(mi.DeclaringType.FullName + "@" + mi.ToString(), mi.DeclaringType.Name + "." + mi.Name);
                            }
                        }
                        bs.DataSource = dt;
                        lbSelected.DisplayMember = COLUMN_DESCRIPTION;
                        lbSelected.ValueMember = COLUMN_VALUE;
                        lbSelected.DataSource = bs;
                        labelSelected.Text = string.Format("Selected Methods (Count: {0})", lbSelected.Items.Count);
                    }
                }
            }
            else
            {
                ClearLists();
            }
        }

        public void End()
        {

        }

        public MethodPickerEditorUI()
        {
            InitializeComponent();
        }

        private void btnDialog_Click(object sender, EventArgs e)
        {
            openFileDialogAssembly.InitialDirectory = Environment.SystemDirectory;
            if (openFileDialogAssembly.ShowDialog() == DialogResult.OK)
            {
                ClearLists();
                tbAssemblyName.Text = openFileDialogAssembly.FileName;

                LoadAssembly();

                return;
            }

        }

        private bool LoadAssembly()
        {
            try
            {
                assembly = Assembly.LoadFrom(tbAssemblyName.Text);
                assemblyTypes = assembly.GetTypes();
                labelTypes.Text = string.Format("Types (Count: {0})", assemblyTypes.Length);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Cannot load assembly. \r\n" + ex.Message);
                return false;
            }
            lbTypes.ClearSelected();
            lbTypes.SelectedIndexChanged -= lbTypes_SelectedIndexChanged;
            lbTypes.DataSource = assemblyTypes;
            lbTypes.DisplayMember = "Name";
            lbTypes.SelectedIndexChanged += lbTypes_SelectedIndexChanged;
            return true;
        }

        private void lbTypes_SelectedIndexChanged(object sender, EventArgs e)
        {
            BindMethods();
        }

        private void BindMethods()
        {
            if (lbTypes.SelectedIndex > 0)
            {
                Type selectedType = lbTypes.SelectedItem as Type;
                lbMethods.DataSource = null;
                lbMethods.Items.Clear();
                lbMethods.SelectedIndex = -1;
                MethodInfo[] methods = selectedType.GetMethods(_bindingFlags);
                lbMethods.DataSource = methods;
                labelMethods.Text = string.Format("Methods (Count: {0})", methods.Length);
            }
        }

        private void ClearLists()
        {
            lbTypes.DataSource = null;
            lbTypes.Items.Clear();
            lbTypes.SelectedIndex = -1;
            lbMethods.DataSource = null;
            lbMethods.Items.Clear();
            lbMethods.SelectedIndex = -1;
            lbSelected.DataSource = null;
            lbSelected.Items.Clear();
            lbSelected.SelectedIndex = -1;
            bs.DataSource = null;
        }

        #region *** Add Selected Methods ***
        private void lbMethods_DoubleClick(object sender, EventArgs e)
        {
            if (lbTypes.SelectedItem != null && lbMethods.SelectedItem != null)
            {
                AddSelectedMethods();
            }

        }

        private void lbMethods_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Return)
            {
                AddSelectedMethods();
            }
        }

        private void btnSelect_Click(object sender, EventArgs e)
        {
            AddSelectedMethods();
        }

        private void AddSelectedMethods()
        {
            if (lbMethods.SelectedItems != null && lbTypes.SelectedItem != null)
            {
                Type selectedType = lbTypes.SelectedItem as Type;
                DataTable dt = bs.DataSource as DataTable;
                if (dt == null)
                {
                    dt = new DataTable();
                    dt.Columns.Add(COLUMN_VALUE);
                    dt.Columns.Add(COLUMN_DESCRIPTION);
                }
                foreach (MethodInfo mi in lbMethods.SelectedItems)
                {
                    dt.Rows.Add(mi.DeclaringType.FullName + "@" + mi.ToString(), mi.DeclaringType.Name + "." + mi.Name);
                }
                bs.DataSource = dt;
                lbSelected.DisplayMember = COLUMN_DESCRIPTION;
                lbSelected.ValueMember = COLUMN_VALUE;
                lbSelected.DataSource = bs;
                labelSelected.Text = string.Format("Selected Methods (Count: {0})", lbSelected.Items.Count);
            }
        }
        #endregion

        private void okButton_Click(object sender, EventArgs e)
        {
            ReturnValue = tbAssemblyName.Text + "#" + GetSelectedMethodsIds();
        }

        private string GetSelectedMethodsIds()
        {
            StringBuilder sbMethodGuids = new StringBuilder();
            foreach (DataRowView drvSelectedMethod in lbSelected.Items)
            {
                sbMethodGuids.AppendFormat("{0}#", drvSelectedMethod[COLUMN_VALUE].ToString());
            }
            sbMethodGuids.Remove(sbMethodGuids.Length - 1, 1);
            return sbMethodGuids.ToString();
        }

        private MethodInfo GetMethodInfoBySignature(string methodSignature)
        {
            string[] classAndMethodNames = methodSignature.Split(new char[] {'@'});
            if (classAndMethodNames.Length == 2)
            {
                foreach(Type type in assemblyTypes)
                {
                    if (type.FullName == classAndMethodNames[0])
                    {
                        foreach(MethodInfo methodInfo in type.GetMethods())
                        {
                            if(methodInfo.ToString() == classAndMethodNames[1])
                                return methodInfo;
                        }
                    }
                }
            }
            return null;
        }

        private void linkToBlog_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            System.Diagnostics.Process.Start("http://martinolivarespick.blogspot.com/");
        }

        #region *** Delete Selected Methods ***
        private void DeleteSelectedMethods()
        {
            if (lbSelected.SelectedItems != null)
            {
                DataTable dt = bs.DataSource as DataTable;
                if (dt != null)
                {
                    List<string> keysToDelete = new List<string>();
                    foreach (DataRowView selectedItem in lbSelected.SelectedItems)
                    {
                        keysToDelete.Add(selectedItem[COLUMN_VALUE].ToString());
                    }
                    foreach (string methodGuid in keysToDelete)
                    {
                        foreach (DataRow row in dt.Rows)
                        {
                            if (row[COLUMN_VALUE].ToString() == methodGuid)
                            {
                                dt.Rows.Remove(row);
                                break;
                            }
                        }
                    }
                    bs.DataSource = dt;
                }

                labelSelected.Text = string.Format("Selected Methods (Count: {0})", lbSelected.Items.Count);
            }
        }

        private void btnDeleteSelectedMethods_Click(object sender, EventArgs e)
        {
            DeleteSelectedMethods();
        }

        private void lbSelected_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                DeleteSelectedMethods();
            }
        }

        #endregion

        #region *** Binding Flags ***
        private void chkDeclaredOnly_CheckedChanged(object sender, EventArgs e)
        {
            if (chkDeclaredOnly.Checked)
            { _bindingFlags |= BindingFlags.DeclaredOnly; }
            else { _bindingFlags &= ~BindingFlags.DeclaredOnly; }
            BindMethods();
        }

        private void chkPublic_CheckedChanged(object sender, EventArgs e)
        {
            if (chkPublic.Checked)
            { _bindingFlags |= BindingFlags.Public; }
            else { _bindingFlags &= ~BindingFlags.Public; }
            BindMethods();
        }

        private void chkNonPublic_CheckedChanged(object sender, EventArgs e)
        {
            if (chkNonPublic.Checked)
            { _bindingFlags |= BindingFlags.NonPublic; }
            else { _bindingFlags &= ~BindingFlags.NonPublic; }
            BindMethods();
        }

        private void chkStatic_CheckedChanged(object sender, EventArgs e)
        {
            if (chkStatic.Checked)
            { _bindingFlags |= BindingFlags.Static; }
            else { _bindingFlags &= ~BindingFlags.Static; }
            BindMethods();
        }
        #endregion


    }
}
